package indi.wshape1.takeawaydeliveryinfo.dao.impl;

import indi.wshape1.takeawaydeliveryinfo.dao.BaseBAO;
import indi.wshape1.takeawaydeliveryinfo.domain.Status;
import indi.wshape1.takeawaydeliveryinfo.util.ConnectionUtil;
import indi.wshape1.takeawaydeliveryinfo.util.StringUtil;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author Wshape1
 * @create 2022-11-07 18:50
 */

public class BaseDAOImpl<T> implements BaseBAO<T> {

    private final Class<?> entityClass;


    public BaseDAOImpl() {
        Type genericType = getClass().getGenericSuperclass();
        Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
        Type actualType = actualTypeArguments[0];
        try {
            entityClass = Class.forName(actualType.getTypeName());
        } catch (ClassNotFoundException e) {
            throw new RuntimeException();
        }
    }

    @Override
    public Connection getConnection() {
        return ConnectionUtil.getConnection();
    }

    @Override
    public void close(Connection connection) {
        //为空， 交给事物   关闭
        try {
            ConnectionUtil.closeConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
//        if (connection != null)
//            try {
//                connection.close();
//            } catch (SQLException e) {
//                throw new RuntimeException(e);
//            }
    }

    @Override
    public void close(Statement statement) {
        if (statement != null)
            try {
                statement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
    }

    @Override
    public void close(ResultSet resultSet) {
        if (resultSet != null)
            try {
                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
    }

    @Override
    public List<T> executeQuery(String sql, Object... params) {
        List<T> list = new ArrayList<>();
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet resultSet = null;
        try {

            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            setParams(pstmt, params);
            resultSet = pstmt.executeQuery();
            ResultSetMetaData metaData = resultSet.getMetaData();

            int colCount = metaData.getColumnCount();

            while (resultSet.next()) {
                T entity = (T) entityClass.getDeclaredConstructor().newInstance();

                for (int i = 1; i <= colCount; i++) {
                    String columnName = metaData.getColumnName(i);
                    Object columnValue = resultSet.getObject(i);
                    setValue(entity, columnName, columnValue);
                }
                list.add(entity);

            }

        } catch (SQLException | InvocationTargetException | InstantiationException | IllegalAccessException |
                 NoSuchMethodException e) {
            throw new RuntimeException(e);
        } finally {
            close(resultSet);
            close(pstmt);
            close(conn);
        }

        return list;
    }

    public Object[] executeComplexQuery(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            setParams(pstmt, params);
            rs = pstmt.executeQuery();

            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            Object[] columnValueArr = new Object[columnCount];
            if (rs.next()) {
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);
                    columnValueArr[i] = columnValue;
                }
                return columnValueArr;
            }
        } catch (SQLException e) {
            throw new RuntimeException();
        } finally {
            close(rs);
            close(pstmt);
            close(conn);
        }
        return null;
    }

    @Override
    public int executeUpdate(String sql, Object... params) throws SQLException {
        boolean insertFlag = false;
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet resultSet = null;
        insertFlag = sql.trim().toUpperCase().startsWith("INSERT");
//        try {
        conn = getConnection();
        if (insertFlag) {
            pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        } else {
            pstmt = conn.prepareStatement(sql);
        }
        setParams(pstmt, params);
        int count = pstmt.executeUpdate();

        if (insertFlag) {
            resultSet = pstmt.getGeneratedKeys();
            if (resultSet.next()) {
                return ((Long) resultSet.getLong(1)).intValue();
            }
        }
        close(resultSet);
        close(pstmt);
//        close(conn);
        return count;
//        } catch (SQLException e) {
//            throw new RuntimeException(e);
//        } finally {

//        }
    }


    private void setParams(PreparedStatement statement, Object... params) throws SQLException {
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                statement.setObject(i + 1, params[i]);
            }
        }
    }


    private void setValue(Object obj, String property, Object propertyValue) {
        Class<?> clazz = obj.getClass();
        try {
            Field field = clazz.getDeclaredField(property);
            field.setAccessible(true);

            if ("time".equals(property)) {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                String timeString = formatter.format((LocalDateTime) propertyValue);
                Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(timeString);
                field.set(obj, date);
            } else if (!"status".equals(property)) {
                field.set(obj, propertyValue);
            } else {
                Status status;
                if (StringUtil.isSendingStatus((String) propertyValue)) {
                    status = Status.SENDING;
                } else {
                    status = Status.SENT;
                }
                field.set(obj, status);
            }
        } catch (NoSuchFieldException | IllegalAccessException | ParseException e) {
            throw new RuntimeException(e);
        }
    }

}
